package com.marver.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.marver.common.Audience;
import com.marver.common.enumeration.IpTypEnum;
import com.marver.common.filter.MyRequestWrapper;
import com.marver.common.filter.MyResponseWrapper;
import com.marver.common.result.Result;
import com.marver.common.result.ResultStatus;
import com.marver.common.utils.*;
import com.marver.entity.Log;
import com.marver.sys.service.LogService;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.http.HttpServletRequestWrapper;
import com.netflix.zuul.http.ServletInputStreamWrapper;
import io.jsonwebtoken.Claims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
@Component
public class AccessFilter extends ZuulFilter  {
    @Autowired
    private Audience audienceEntity;
    @Autowired
    private LogService logService;

    private static Logger log = LoggerFactory.getLogger(AccessFilter.class);
    private Result resultMsg;

    @Override
    public String filterType() {
        return "pre";
    }

    @Override

    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        //过滤options请求
        if("OPTIONS".equals(request.getMethod())){
            return null;
        }
        StringBuilder logInfo = new StringBuilder();

        logInfo.append("\n\t\t=========请求地址 : " + request.getRequestURL().toString()+" =========\n");
        logInfo.append("\t\t=========HTTP METHOD : " + request.getMethod()+"=========\n");
        logInfo.append("\t\t=========IP : " + HttpUtil.getClientIP(request)+"=========\n");
        logInfo.append("\t\t=========URL : " + request.getRequestURL().toString()+"=========\n");
        try {
            logInfo.append("\t\t=========请求参数"+StreamUtils.copyToString(request.getInputStream(), Charset.forName("UTF-8"))+"=========");
        } catch (IOException e) {
        }

        log.info(logInfo.toString());
        //过滤获取平台token
        if("/platform/oauth/token".equals(request.getRequestURI())){
            return null;
        }
        try {
            //验证系统token
            if(!preHandle(ctx)){
                ObjectMapper mapper = new ObjectMapper();
                HttpServletResponse response = ctx.getResponse();
                response.getOutputStream().write(mapper.writeValueAsBytes(resultMsg));

                ctx.setSendZuulResponse(false);
                ctx.setResponseStatusCode(200);
                ctx.setResponse(response);
                resultMsg = new Result();
                return null;
            }
        } catch (Exception e) {
            log.error(e.getMessage());
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            ctx.setResponseBody(e.getMessage());
        }

        log.info("access token ok");
        return null;
    }


    public boolean preHandle(RequestContext ctx) throws Exception {
        HttpServletRequest request = ctx.getRequest();

        //备份HttpServletRequest。解決ServletInputStream重复读取问题
        MyRequestWrapper requestWrapper = new MyRequestWrapper(request);
        MyResponseWrapper responseWrapper = new MyResponseWrapper(ctx.getResponse());
        ObjectMapper mapper = new ObjectMapper();

        String token = request.getHeader("accessToken");
        if (StringUtils.isEmpty(token)) {
            resultMsg = new Result(ResultStatus.invalid_token.getErrorCode());
            return false;
        }

        String requestParam="";

        //獲取Body傳過來的參數（ajax data提交上来的参数）-----------開始
        StringBuffer jb = new StringBuffer();
        String line = null;
        BufferedReader reader = requestWrapper.getReader();
        while ((line = reader.readLine()) != null){
            jb.append(line);
        }
        requestParam = jb.toString();
        //獲取Body傳過來的參數（ajax data提交上来的参数）-----------結束

        //獲取url參數-------開始
        Enumeration enu=request.getParameterNames();
        String apiAdderss=requestWrapper.getRequestURL()+"?";
        while(enu.hasMoreElements()){
            String paraName=(String)enu.nextElement();
            apiAdderss=apiAdderss+paraName+"="+request.getParameter(paraName)+"&";
        }
        apiAdderss = apiAdderss.substring(0, apiAdderss.length()-1);
        String requestIp = HttpUtil.getClientIP(request);


        /**
         * 限制黑白名单访问
         */
        Claims claims = JwtHelper.parseJWT(token, audienceEntity.getBase64Secret());
        if(claims!=null){
            String appId = claims.get("appid").toString();
            String blackIp  = JedisUtils.get(IpTypEnum.BLACK_IP.getKey()+appId);
            String whiteIp = JedisUtils.get(IpTypEnum.WHITE_IP.getKey()+appId);
            //包含在黑名单中则不返回信息
            if(StringUtils.isNotEmpty(blackIp)){
                if(CheckIpUtils.checkAccessIP(requestIp,blackIp)){
                    return false;
                }
            }
            //不包含在白名单中则不返回信息
            if(StringUtils.isNotEmpty(whiteIp)){
                if(!CheckIpUtils.checkAccessIP(requestIp,whiteIp)){
                    return false;
                }
            }
            return accessToken(requestIp, claims, responseWrapper, requestParam, apiAdderss);
        }

        resultMsg = new Result(ResultStatus.invalid_token.getErrorCode());
        logService.addLog(requestIp, "", apiAdderss, requestParam,mapper.writeValueAsString(resultMsg));
        return false;
    }

    /**
     * token验证
     * @param requestIp
     * @param claims
     * @param responseWrapper
     * @param requestParam
     * @param apiAdderss
     * @return
     * @throws IOException
     */
    private boolean accessToken(String requestIp, Claims claims, MyResponseWrapper responseWrapper, String requestParam, String apiAdderss) throws IOException {
        byte[] responseBody;
        //判断获取Token的IP与现在请求的IP是否一致，如果不一致则报安全隐患
        if(claims.containsKey("ip")&&
                claims.get("ip").toString().equals(requestIp)){
            //寫日誌
            Log log = logService.addLog(requestIp, claims.getIssuer(), apiAdderss, requestParam);
            responseBody = responseWrapper.getResponseData();
            //更新日誌的輸出
            logService.updateLog(log.getId(), new String(responseBody));
        }else{
            resultMsg = new Result(ResultStatus.FAIL.getErrorCodeToString(),"IP发生变化,重新获取token");
            return false;
        }
        return true;
    }
}
